<?php

namespace services;

use repositories\PaymentRepositories;
use EasyWeChat\Factory;
use EasyWeChat\Kernel\Exceptions\InvalidArgumentException;
use EasyWeChat\Kernel\Exceptions\InvalidConfigException;
use EasyWeChat\Kernel\Support\Collection;
use EasyWeChat\Payment\Application;
use GuzzleHttp\Exception\GuzzleException;
use Psr\Http\Message\ResponseInterface;
use think\facade\Route as Url;
use utils\Hook;

/**
 * @title 微信小程序的接口
 * Class MiniProgramService
 * @package services
 */
class MiniProgramService
{
    /**
     * @title 单例
     * @var null
     */
    private static $instance = null;


    /**
     * @title 设置EasyWeChat的配置方法
     */
    public static function options()
    {
        $wechat = SystemConfigService::more(['site_url', 'routine_appId', 'routine_appsecret']);
        $payment = SystemConfigService::more(['pay_routine_mchid', 'pay_routine_key', 'pay_routine_client_cert', 'pay_routine_client_key', 'pay_weixin_open']);
        $config = [];
        $config['mini_program'] = [
            'app_id' => isset($wechat['routine_appId']) ? trim($wechat['routine_appId']) : '',
            'secret' => isset($wechat['routine_appsecret']) ? trim($wechat['routine_appsecret']) : '',
            'token' => isset($wechat['wechat_token']) ? trim($wechat['wechat_token']) : '',
            'aes_key' => isset($wechat['wechat_encodingaeskey']) ? trim($wechat['wechat_encodingaeskey']) : ''
        ];
        $config['payment'] = [
            'app_id' => isset($wechat['routine_appId']) ? trim($wechat['routine_appId']) : '',
            'mch_id' => trim($payment['pay_routine_mchid']),
            'key' => trim($payment['pay_routine_key']),
            'cert_path' => realpath('.' . $payment['pay_routine_client_cert']),
            'key_path' => realpath('.' . $payment['pay_routine_client_key']),
            'notify_url' => $wechat['site_url'] . Url::buildUrl('/api/routine/notify')->suffix(false)->build()
        ];
        return $config;
    }

    /**
     * @title 静态方法|生成EasyWeChat的单例
     * @param bool $cache
     * @return \EasyWeChat\MiniProgram\Application|null
     */
    public static function application($cache = false)
    {
        $config = [
            'app_id' => 'wx686d9d1f3360b556',
            'secret' => '60e031c7e4443ea3bdbc0deca5b1639f',

            // 下面为可选项
            // 指定 API 调用返回结果的类型：array(default)/collection/object/raw/自定义类名
            'response_type' => 'array',

            'log' => [
                'level' => 'debug',
                'file' => __DIR__ . '/wechat.log',
            ],
        ];


        (self::$instance == null || $cache == true) && (self::$instance = Factory::miniProgram($config));
        return self::$instance;
    }

    /**
     * 获取accessToken
     * @return array
     * @throws InvalidArgumentException
     * @throws InvalidConfigException
     * @throws \EasyWeChat\Kernel\Exceptions\HttpException
     * @throws \EasyWeChat\Kernel\Exceptions\RuntimeException
     * @throws \Psr\SimpleCache\InvalidArgumentException
     */
    public static function getAccessToken()
    {
        return Factory::officialAccount(self::options()["mini_program"])->access_token->getToken();
    }


    /**
     * @title 获取用户登录的时候的用户信息
     * @param string $code
     * @return array|Collection|object|ResponseInterface|string
     * @throws InvalidConfigException
     */
    public static function getUserInfo(string $code)
    {
        return self::application()->auth->session($code);
    }

    /**
     * @title 加密数据的解密
     * @param $sessionKey
     * @param $iv
     * @param $encryptData
     * @return array
     * @throws \EasyWeChat\Kernel\Exceptions\DecryptException
     */
    public static function encryptor($sessionKey, $iv, $encryptData)
    {
        return self::application()->encryptor->decryptData($sessionKey, $iv, $encryptData);
    }

    /**
     * @title 客服消息
     */
    public static function staffService()
    {
        return self::application()->customer_service;
    }

    /**
     * @title 生成小程序的二维码
     */
    public static function qrcodeService()
    {
        return self::application()->app_code;
    }

    /**
     *微信小程序二维码生成接口不限量永久
     * @param string $scene
     * @param array $optional
     * @return array|Collection|object|ResponseInterface|string
     */
    public static function appCodeUnlimitService(string $scene, array $optional = ['page' => null, 'width' => 600])
    {
        return self::application()->app_code->getUnlimit($scene, $optional);
    }

    /**
     * @title 模板消息接口
     * @return \EasyWeChat\MiniProgram\TemplateMessage\Client
     */
    public static function noticeService()
    {
        return self::application()->template_message;
    }

    /**
     * @title 订阅模板消息接口
     */
    public static function subscribeNoticeService()
    {
        return self::application()->subscribe_message;
    }

    /**
     * @title 发送模板消息
     * @param array $data
     * @return array|Collection|object|ResponseInterface|string
     * @throws InvalidArgumentException
     * @throws InvalidConfigException
     * @throws GuzzleException
     */
    public static function sendTemplate(array $data)
    {
        return self::noticeService()->send($data);
    }

    /**
     * @title 发送订阅消息
     * @param array $data
     * @return array|Collection|object|ResponseInterface|string
     * @throws InvalidArgumentException
     * @throws InvalidConfigException
     * @throws GuzzleException
     */
    public static function sendSubscribeMessage(array $data)
    {
        return self::subscribeNoticeService()->send($data);
    }

    /**
     * @title 支付接口
     * @return Application
     */
    public static function paymentService()
    {
        $payment = [
            "app_id" => 'wx686d9d1f3360b556',
            'mch_id' => 1602484692,
            'key' => 'MBZNZBWHYXGS91420100MA49CAEY2020',
            'notify_url' => 'localhost:8000/api/routine/notify'
        ];
        return Factory::payment($payment);
    }

    /**
     * @title 设置子商户支付 【】
     * @param string $sub_merchant_id
     * @param string $sub_app_id
     * @return Application
     */
    public static function subPaymentService(string $sub_merchant_id, string $sub_app_id)
    {
        return Factory::payment(self::options())->setSubMerchant($sub_merchant_id, $sub_app_id);
    }

    /**
     * @title 生成支付订单对象  【H5 支付，公众号支付，扫码支付，支付中签约】
     * @param array $param
     * body => "支付说明"; out_trade_no => 订单编号；total_fee => 金额；[spbill_create_ip] => 'IP'；notify_url => 支付结果通知网址；trade_type => 请对应换成你的支付方式对应的值类型;openid => openid
     * 实例：[ 'body' => '腾讯充值中心-QQ会员充值','out_trade_no' => '20150806125346', 'total_fee' => 88,'spbill_create_ip' => '123.12.12.123', 'notify_url' => 'https://pay.weixin.qq.com/wxpay/pay.action', 'trade_type' => 'JSAPI','openid' => 'oUpF8uMuAJO_M2pxb1Q9zNjWeS6o',]
     * @param bool $isContract 【是否支付中签约，默认 false】 若为true 则需要加入参数 【plan_id：协议模板id，contract_code：签约协议号，contract_display_account, 签约用户的名称,contract_notify_url:网址】
     * @return array|Collection|object|ResponseInterface|string
     * @throws InvalidArgumentException
     * @throws InvalidConfigException
     * @throws GuzzleException
     */
    public static function paymentOrder(array $param, $isContract = false)
    {
        return self::paymentService()->order->unify($param, $isContract);
    }

    /**
     * @title 查询订单
     * @param string $out_trade_no 订单编号
     * @return array|Collection|object|ResponseInterface|string
     * @throws InvalidArgumentException
     * @throws InvalidConfigException
     */
    public static function queryOrderByOrderId(string $out_trade_no)
    {
        return self::paymentService()->order->queryByOutTradeNumber($out_trade_no);
    }

    /**
     * @title 更加微信订单的ID查询
     * @param string $transaction_id
     * @return array|Collection|object|ResponseInterface|string
     * @throws InvalidArgumentException
     * @throws InvalidConfigException
     */
    public function queryOrderByTransactionId(string $transaction_id)
    {
        return self::paymentService()->order->queryByTransactionId($transaction_id);
    }

    /**
     * @title 关闭订单  【注意：订单生成后不能马上调用关单接口，最短调用时间间隔为5分钟】
     * @param string $out_trade_no
     * @return array|Collection|object|ResponseInterface|string
     * @throws InvalidArgumentException
     * @throws InvalidConfigException
     * @throws GuzzleException
     */
    public static function closeOrder(string $out_trade_no)
    {
        return self::paymentService()->order->close($out_trade_no);
    }

    /**
     * @title 使用商户订单号退款
     * @param string $transactionId 微信订单号
     * @param string $refundNumber 商户退款单号
     * @param int $totalFee 订单金额
     * @param int $refundFee 退款金额
     * @param array $config 其他参数
     * @return array|Collection|object|ResponseInterface|string
     * @throws InvalidConfigException
     */
    public static function refund(string $transactionId, string $refundNumber, int $totalFee, int $refundFee, array $config = [])
    {
        $totalFee = floatval($totalFee);
        $refundFee = floatval($refundFee);
        return self::paymentService()->refund->byTransactionId($transactionId, $refundNumber, $totalFee, $refundFee, $config);
    }

    /**
     * @title 根据订单号退款
     * @param string $number
     * @param string $refundNumber
     * @param int $totalFee
     * @param int $refundFee
     * @param array $config
     * @return array|Collection|object|ResponseInterface|string
     * @throws InvalidConfigException
     */
    public static function payOrderRefund(string $number, string $refundNumber, int $totalFee, int $refundFee, array $config = [])
    {
        if (empty($totalFee)) exception("缺少必要参数：total Fee");
        return self::paymentService()->refund->byOutTradeNumber($number, $refundNumber, $totalFee, $refundFee, $config);
    }

    /**
     * @title 微信支付成功回调接口
     */
    public static function handleNotify()
    {
        self::paymentService()->handlePaidNotify(function ($notify, $successful) {
            if ($successful && isset($notify->out_trade_no)) {
                if (isset($notify->attach) && $notify->attach) {
                    if (($count = strpos($notify->out_trade_no, '_')) !== false) {
                        $notify->out_trade_no = substr($notify->out_trade_no, $count + 1);
                    }
                    return (new Hook(PaymentRepositories::class, 'wechat'))->listen($notify->attach, $notify->out_trade_no);
                }
            }
            return false;
        });
    }

    /**
     * @title 发送客服消息
     * @param array $message
     * @throws InvalidConfigException
     * @throws GuzzleException
     */
    public static function staffTo(array $message)
    {
        self::staffService()->send($message);
    }

    /**
     * @title 敏感信息过滤服务器
     */
    public static function securityService()
    {
        return self::application()->content_security;
    }

    /**
     * @title 图片检测
     * @param string $image 图片的路径
     * @return array|Collection|mixed|object|ResponseInterface|string
     * @throws InvalidConfigException
     * @throws GuzzleException
     */
    public static function imageSecurity(string $image)
    {
        $data = self::securityService()->checkImage($image);
        return self::checkContent($data);
    }

    /**
     * @title 文本检测
     * @param string $content
     * @return bool
     * @throws InvalidConfigException
     * @throws GuzzleException
     */
    public static function contentSecurity(string $content)
    {
        $data = self::securityService()->checkText($content);
        return self::checkContent($data);
    }


    /**
     * @title 检测
     * @param Collection $data
     * @return bool
     */
    private static function checkContent(Collection $data)
    {
        $data = !is_array($data) && $data = json_decode($data, true);
        $errCode = $data["errcode"];
        if ($errCode != 0) {
            return false;
        }
        return true;
    }
}